import time
from datetime import datetime
from typing import Optional, Union, Tuple, List

from extensions import logger
from .base import BaseRedis


class RedisOp(BaseRedis):

    async def get_lock(self, name: str, timeout: int) -> Tuple[bool, Union[str, None]]:
        """get the lock"""

        current_time = time.time()
        current_value = current_time + timeout

        lock = await self.set(name=name, value=current_value, ex=timeout, nx=True)
        if lock:
            # origin is not exists now set it OK
            return True, str(current_value)

        # it had exists, get_old_value
        old_lock = await self.get(name=name)

        if old_lock and current_time > float(old_lock):
            # had expired
            # set new and get old
            old_value = await self.set(name=name, value=current_value, ex=timeout, get=True)
            if not old_value or old_lock == old_value:
                return True, str(current_value)
            return False, None
        return False, None

    async def get_open_ai_lock(self, name: str, tokens: int) -> bool:
        """get the openAI lock, 以 key为name"""

        now = datetime.now().strftime('%Y-%m-%d-%H-%M')

        frequency_name, token_name = f'{name}:{now}:frequency', f'{name}:{now}:tokens'

        try:
            frequency = await self.get(name=frequency_name)
            if not frequency:
                frequency = 0
            else:
                frequency = int(frequency)
            if frequency >= 720:
                logger.warning(f'Get OpenAI Lock 失败, 超过次数')
                return False
            await self._incr_by(name=frequency_name, amount=1)

            used_tokens = await self.get(name=token_name)
            if not used_tokens:
                used_tokens = 0
            else:
                used_tokens = int(used_tokens)
            if used_tokens >= 120000:
                logger.warning(f'Get OpenAI Lock 失败, 超过tokens')
                return False
            await self._incr_by(name=token_name, amount=tokens)
        except Exception as exc:
            logger.error(f'Get OpenAI Lock error, exc:{exc}')
            return False

        return True

    async def get_params(self, name: str, keys: Optional[List[str]] = None):
        """获取 hash params """

        try:
            if not await self.exists(name=name):
                return False, None

            response = await self._hash_get_values(name=name, keys=keys)

            return True, response
        except Exception as exc:
            logger.error(f'GetRedisCache:Key:{name},error:{exc}')
            return False, None

    async def set_params(self, name: str, payload: dict, expiry: int = -1):
        """缓存 hash params
        """

        params = dict()
        for key, value in payload.items():
            if value is not None:
                params[key] = value
            if isinstance(value, bool):
                params[key] = int(value)

        if not params:
            return True

        try:
            await self._hash_set(name=name, mapping=params)
            if expiry > 0:
                await self.expire(name=name, seconds=expiry)
            return True
        except Exception as exc:
            logger.error(f'SetRedisCache:Key:{name},error:{exc}')
            return False

    async def del_key(self, name: str, keys: list):
        """删除 hash 中的 key"""

        try:
            await self._hash_del_key(name=name, keys=keys)
        except Exception as exc:
            logger.error(f'DelKey: Name:{name}, Key:{keys}, error: {exc}')

    async def increasing(self, name: str, value: Optional[int] = 1, expiry: Optional[int] = None):

        try:
            await self._incr_by(name=name, amount=value)
            if expiry:
                await self.expire(name=name, seconds=expiry)
            return True
        except Exception as exc:
            logger.warning(f'IncrRedisCache:Key:{name}, warning:{exc}, continue')
            return False


redis_op = RedisOp()
